import json
import threading
import time
from unittest.mock import patch
from django.test import TestCase
from rest_framework.test import APIClient
from users.models import User
from courses.models import Course, Teacher


class OrderCreateIdempotencyTests(TestCase):
    def setUp(self):
        self.client = APIClient()
        # 创建用户并登录（简单方式：直接设置force_authenticate）
        self.user = User.objects.create_user(username='u1', password='pass123456')
        self.client.force_authenticate(user=self.user)

        # 创建必要外键：讲师
        self.teacher = Teacher.objects.create(
            name='t', role=0, title='tt', signature='sg', avatar='av', brief='br'
        )

        # 创建上架的付费课程
        self.course = Course.objects.create(
            name='测试课程', brief='b', teacher=self.teacher, students=0, lessons=0, pub_lessons=0,
            course_type=2, original_price=199, price=199, course_img='img', status=1, level=1
        )

        self.payload = {
            "courseList": [
                {"course_id": self.course.id, "expire_time": 0, "expire_text": "永久有效", "price": float(self.course.price)}
            ],
            "pay_method": 1,
            "use_coupon": False,
            "use_coupon_id": None,
            "total_price": float(self.course.price),
            "real_price": float(self.course.price)
        }

        # 构造一个简单的内存版Redis，替代真实Redis连接
        class _FakeRedis:
            def __init__(self):
                self._kv = {}
                self._hash = {}

            # string
            def set(self, key, value, nx=False, ex=None):
                if nx and key in self._kv:
                    return False
                # 模拟redis-py编码为bytes
                if isinstance(value, str):
                    value = value.encode('utf-8')
                self._kv[key] = value
                return True

            def get(self, key):
                return self._kv.get(key)

            def delete(self, key):
                if key in self._kv:
                    del self._kv[key]
                    return 1
                return 0

            # hash
            def hset(self, name, key, value):
                if isinstance(value, str):
                    value = value.encode('utf-8')
                self._hash.setdefault(name, {})[key] = value
                return 1

            def hget(self, name, key):
                return self._hash.get(name, {}).get(key)

            def hdel(self, name, key):
                bucket = self._hash.setdefault(name, {})
                if key in bucket:
                    del bucket[key]
                    return 1
                return 0

            def hexists(self, name, key):
                return key in self._hash.get(name, {})

        self.fake_redis = _FakeRedis()
        # 预写入购物车
        cart_key = f"code_env_spaces:carts:cartList:{self.user.id}"
        self.fake_redis.hset(cart_key, f"course_{self.course.id}", json.dumps({
            "name": self.course.name,
            "image": self.course.course_img,
            "is_selected": 1,
            "price": float(self.course.price),
            "quantity": 1,
        }))

    def test_idempotency_key_returns_same_result_on_retry(self):
        key = 'test-idemp-1'
        url = '/orders/create/'
        with patch('orders.views.get_redis_connection', return_value=self.fake_redis), \
             patch('django.db.transaction.on_commit', side_effect=lambda func: func()):
            r1 = self.client.post(url, data=self.payload, format='json', HTTP_IDEMPOTENCY_KEY=key)
        self.assertEqual(r1.status_code, 200)
        self.assertEqual(r1.json().get('code'), 1)
        order_no_1 = r1.json()['data']['order_number']

        with patch('orders.views.get_redis_connection', return_value=self.fake_redis), \
             patch('django.db.transaction.on_commit', side_effect=lambda func: func()):
            r2 = self.client.post(url, data=self.payload, format='json', HTTP_IDEMPOTENCY_KEY=key)
        self.assertEqual(r2.status_code, 200)
        self.assertEqual(r2.json().get('code'), 1)
        order_no_2 = r2.json()['data']['order_number']
        self.assertEqual(order_no_1, order_no_2)

    def test_lock_blocks_creation_when_locked(self):
        url = '/orders/create/'
        lock_key = f"code_env_spaces:orders:lock:create:{self.user.id}"
        # 预先加锁，模拟其它请求正在创建订单
        self.fake_redis.set(lock_key, '1', nx=True)
        with patch('orders.views.get_redis_connection', return_value=self.fake_redis):
            resp = self.client.post(url, data=self.payload, format='json', HTTP_IDEMPOTENCY_KEY='k2')
        self.assertEqual(resp.status_code, 429)
        self.assertEqual(resp.json().get('code'), 0)


# Create your tests here.
